package com.jmb.api.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jmb.api.common.exception.LeaseException;
import com.jmb.api.common.exception.ParseException;
import com.jmb.api.common.result.ResultCodeEnum;
import com.jmb.api.common.utils.*;
import com.jmb.api.core.parser.Parser;
import com.jmb.api.mapper.ApiTestCaseRelyDataMapper;
import com.jmb.api.mapper.RelyDataMapper;
import com.jmb.api.model.entity.ApiTestCaseRelyData;
import com.jmb.api.model.entity.ExecuteInterfaceCaseParam;
import com.jmb.api.model.entity.Project;
import com.jmb.api.model.to.api.rely.ApiTestCaseRelyDataTO;
import com.jmb.api.model.vo.api.ApiTestAssertVO;
import com.jmb.api.model.vo.api.ApiTestCaseInfoVo;
import com.jmb.api.model.vo.api.log.ApiTestCaseExecuteLogVO;
import com.jmb.api.model.vo.api.rely.ApiRelyDataVO;
import com.jmb.api.service.*;
import com.jmb.api.vo.api.ApiTestAssertVo;
import com.jmb.api.vo.api.ApiTestCaseVo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
* @author jinbo
* @description 针对表【api_test_case_rely_data(接口依赖)】的数据库操作Service实现
* @createDate 2025-04-08 13:53:56
*/
@Slf4j
@Service
public class ApiTestCaseRelyDataServiceImpl extends ServiceImpl<ApiTestCaseRelyDataMapper, ApiTestCaseRelyData>
    implements ApiTestCaseRelyDataService{

    @Autowired
    private ApiTestCaseRelyDataMapper apiTestCaseRelyDataMapper;
    @Autowired
    private ApiTestCaseService apiTestCaseService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private UserInfoUtil userInfoUtil;
    @Autowired
    private Parser parser;
    @Autowired
    private ApiTestCaseExecuteLogService executeLogService;
    @Autowired
    private LoginUserInfo loginUserInfo;
    @Autowired
    private ApiTestPreCaseService preCaseService;
    @Autowired
    private RelyDataMapper relyDataMapper;


    @Override
    public void saveApiTestCaseRelyData(ApiTestCaseRelyData relyData, HttpServletRequest request) throws ParseException {
        Integer contentType = relyData.getContentType();
        if (contentType == 0){
            ValidUtil.isJsonPath(relyData.getExtractExpression());

        }else if (contentType ==1){
            ValidUtil.isXmlPath(relyData.getExtractExpression());
        }
        // 判断relyCaseId是否存在
        if (apiTestCaseService.getApiTestCaseByCaseId(relyData.getRelyCaseId()) == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_CASEID_NOT);
        }
        // 判断relyName是否存在
        if (apiTestCaseRelyDataMapper.selectRelyDataByName(relyData.getRelyName()) != null){
            throw new LeaseException(ResultCodeEnum.WEB_API_RELY_NAME_EXIST);
        }
        relyData.setCreateTime(new Date());
        this.checkDependency(relyData);
        String realName = loginUserInfo.getRealName(request);
        Integer userId = loginUserInfo.getUserId(request);
        relyData.setCreatorId(userId);
        relyData.setCreatorName(realName);
        this.save(relyData);

    }


    @Override
    public List<ApiRelyDataVO> findApiTestCaseRelyDataList(IPage<ApiRelyDataVO> page, ApiTestCaseRelyDataTO apiTestCaseRelyDataListTO) {
        IPage<ApiRelyDataVO> relyDataVOIPage = apiTestCaseRelyDataMapper.selectRelyDataList(page, apiTestCaseRelyDataListTO);
        ArrayList<ApiRelyDataVO> list = new ArrayList<>();
        for (ApiRelyDataVO apiRelyDataVO : relyDataVOIPage.getRecords()) {
            // 获取到项目id
            Integer projectId = apiTestCaseService.getApiTestCaseByCaseId(apiRelyDataVO.getRelyCaseId()).getProjectId();
            // 查询项目Url
            Project project = new Project();
            project.setProjectId(projectId);
            Project projectVo = projectService.getProject(project);
            apiRelyDataVO.setCaseUrl(projectVo.getProtocol() + "://" + projectVo.getDomain() + apiRelyDataVO.getCaseUrl());
            list.add(apiRelyDataVO);
        }
        return  list;
    }

    /**
     * 修改接口依赖
     * @param relyData 接口依赖修改的参数
     * @param request 请求
     */
    @Override
    public void modifyIfRelyData(ApiTestCaseRelyData relyData, HttpServletRequest request) throws ParseException {
        Integer userId = userInfoUtil.getUserId(request);
        Integer relyId = relyData.getRelyId();
        ApiRelyDataVO relyDataVO = this.selectRelyDataById(relyId);
        Integer modifiable = relyDataVO.getOthersModifiable();
        Integer creatorId = relyDataVO.getCreatorId();
        if (modifiable == null && creatorId == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_MODIFY_NOT);
        }
        if (!Objects.equals(creatorId, userId)){
            if (modifiable != null && modifiable == 1) {
                throw new LeaseException(ResultCodeEnum.WEB_API_MODIFY_NOT);
            }
            // 当前编辑人与创建人不一致时，不允许修改othersModifiable和othersDeletable字段
            relyData.setOthersModifiable(null);
            relyData.setOthersDeletable(null);
        }
        // 提取type进行验证
        if (relyDataVO.getContentType() == 0){
            ValidUtil.isJsonPath(relyData.getExtractExpression());
        } else if (relyDataVO.getContentType() == 1){
            ValidUtil.isXmlPath(relyData.getExtractExpression());
        }
        // 判断relyCaseId是否存在
        if (apiTestCaseService.getApiTestCaseByCaseId(relyData.getRelyCaseId()) == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_CASEID_NOT);
        }
        // 判断relyName是否存在
        if (apiTestCaseRelyDataMapper.selectRelyDataByIdName(relyData.getRelyName(),relyId) != null){
            throw new LeaseException(ResultCodeEnum.WEB_API_RELY_NAME_EXIST);
        }
        if (!apiTestCaseRelyDataMapper.checkRelyName(relyData).isEmpty()){
            throw new LeaseException(ResultCodeEnum.WEB_API_RELY_NAME_EXIST);
        }
        // 校验依赖
        this.checkDependency(relyData);
        // 修改数据
        LambdaUpdateWrapper<ApiTestCaseRelyData> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(ApiTestCaseRelyData::getRelyId,relyId);
        this.update(updateWrapper);

    }

    @Override
    public void removeIfRelyData(Integer relyId, HttpServletRequest request) {
        // 获取当前用户id
        Integer userId = userInfoUtil.getUserId(request);
        ApiRelyDataVO relyDataVO = this.selectRelyDataById(relyId);
        if (relyDataVO.getOthersModifiable() == null && relyDataVO.getCreatorId() == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_MODIFY_NOT);
        }
        if (!Objects.equals(relyDataVO.getCreatorId(), userId) && relyDataVO.getOthersDeletable() == 1){
            throw new LeaseException(ResultCodeEnum.WEB_API_MODIFY_NOT);
        }
        this.removeById(relyId);

    }

    @Override
    public String checkRelyResult(Integer relyId, String executor) throws ParseException {
        log.info("------------------------------执行接口预检操作------------------------------");
        ApiRelyDataVO relyData = this.selectRelyDataById(relyId);
        if (relyData == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_Rely_DATA_NOT_EXIST);
        }
        Integer caseId = relyData.getRelyCaseId();
        log.info("------------------------------进入无下标取值模式------------------------------");
        // 根据caseId调用相应caseId
        log.info("根据caseId调用相应case，caseId={}", caseId);
        String chainNo = NoUtil.genChainNo();
        Integer executeLogId = apiTestCaseService.executeCase(new ExecuteInterfaceCaseParam(caseId, executor,
                null, chainNo, null,  1, null, null,
                null, null, 1, null, false));
        log.info("调用成功，executeLogId={}",executeLogId);
        // 获取case执行结果，不等于0，则用例未通过
        if (executeLogService.findExecute(executeLogId).getStatus() !=0){
            log.warn("预检接口依赖，前置用例执行未通过，executeLogId={}", executeLogId);
            throw new LeaseException(ResultCodeEnum.WEB_API_PRE_CASE_NOT_PASS);
        }
        // 根据根据executeLogId查询对应的执行记录
        ApiTestCaseExecuteLogVO caseExecuteLogVO = executeLogService.findExecute(executeLogId);
        String responseBody = caseExecuteLogVO.getResponseBody();
        String responseHeaders = caseExecuteLogVO.getResponseHeaders();
        // 根据contentType来确定对何字段进行替换, 提取数据类型   0json/1html/2header/
        Integer contentType = relyData.getContentType();
        String expression = relyData.getExtractExpression();
        try {
            if (contentType == 0){
                // json
                log.info("-----------------------------根据JsonPath取值");
                ArrayList jsonPathArray = JSONObject.parseObject(ParseUtil.parseJson(responseBody, expression), ArrayList.class);
                if (jsonPathArray.isEmpty()){
                    log.warn("预检接口依赖，提取方式为jsonPath，提取内容为空，expression={}", expression);
                    throw new ParseException(expression+"提取内容为空");
                }
                if (jsonPathArray.size()==1){
                    Object o = jsonPathArray.get(0);
                    String s = o == null ? null : o.toString();
                    log.info("提取到的内容是:{}",s);
                    return s;
                }else {
                    log.info("提取到的json内容是:{}", JSONObject.toJSONString(jsonPathArray));
                    return JSONObject.toJSONString(jsonPathArray);
                }
            }else if (contentType == 1 ){
                // html
                log.info("-------------------------提取Xpath取值");
                ArrayList xpathArray = JSONObject.parseObject(ParseUtil.parseXml(responseBody, expression), ArrayList.class);
                if (xpathArray.isEmpty()){
                    log.warn("预检接口依赖，提取方式为xpath，提取内容为空，expression={}", expression);
                    throw new ParseException(expression+"提取内容为空");
                }
                if (xpathArray.size()==1){
                    Object o = xpathArray.get(0);
                    String s = o == null ? null : o.toString();
                    log.info("提取到html内容是:{}",s);
                    return s;
                }else {
                    log.info("提取到的html内容是:{}", JSONObject.toJSONString(xpathArray));
                    return JSONObject.toJSONString(xpathArray);
                }
            }else if (contentType == 2){
                // haaders
                log.info("-------------------------提取Headers取值");
                JSONArray headerArray =(JSONArray) JSONObject.parseObject(responseHeaders, HashMap.class).get(expression);
                if (headerArray == null || headerArray.isEmpty()){
                    log.warn("预件接口依赖，提取方式headers，未找到请求头，expression={}",expression);
                    throw new ParseException("未找到请求头"+expression);
                }else {
                    log.info("提取到的headers内容是:{}", JSONObject.toJSONString(headerArray));
                    if (headerArray.size()==1){
                        Object o = headerArray.get(0);
                        String s = o == null ? null : o.toString();
                        log.info("提取到headers内容是:{}",s);
                        return s;
                    }
                    else {
                        log.info("提取内容为:{}", JSON.toJSONString(headerArray));
                        return JSONObject.toJSONString(headerArray);
                    }
                }
            }else {
                log.warn("不支持该种取值方式，contentType，={}",contentType);
                throw new LeaseException(ResultCodeEnum.WEB_API_TEST_RELY_NOT_SUPPORT);
            }
        }
        catch (ParseException | LeaseException e){
            log.warn("不支持该种取值方式, contentType={}", contentType);
            throw new LeaseException(ResultCodeEnum.WEB_API_TEST_RELY_NOT_SUPPORT);
        }
        catch (Exception e){
            log.error("预检接口依赖出现异常，errorMsg={}", ExceptionUtil.msg(e));
            throw new ParseException(e.getMessage());
        }

    }

    @Override
    public ApiRelyDataVO findRelyDataByIdName(String relyName, Integer relyId) {
        return apiTestCaseRelyDataMapper.selectRelyDataByIdName(relyName, relyId);
    }

    @Override
    public ApiRelyDataVO selectRelyDataById(Integer relyId) {
        return apiTestCaseRelyDataMapper.selectRelyDataById(relyId);
    }

    @Override
    public ApiRelyDataVO selectRelyDataByName(String relyName) {
        if (relyName == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_TEST_RELY_NAME_NOT);
        }
        return apiTestCaseRelyDataMapper.selectRelyDataByName(relyName);
    }
    /**
     * 检查依赖名称合理性
     * @param ifRelyDataDO ifRelyDataDO
     */
    private void checkDependency(ApiTestCaseRelyData ifRelyDataDO) throws ParseException {
        // 如果是接口依赖，确保接口依赖本身的headers、params、form-data、form-data-encoded、raw、断言预期结果不包含当前依赖名称
        ApiTestCaseVo caseByCaseInfo = apiTestCaseService.getApiTestCaseByCaseId(ifRelyDataDO.getRelyCaseId());
        if (caseByCaseInfo == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_Rely_DATA_NOT_EXIST);
        }
        String name = ifRelyDataDO.getRelyName();

        String headers = caseByCaseInfo.getHeaders();
        String params = caseByCaseInfo.getParams();
        String formData = caseByCaseInfo.getFormData();
        String formDataEncoded = caseByCaseInfo.getFormDataEncoded();
        String raw = caseByCaseInfo.getRaw();
        List<ApiTestAssertVo> assertList = caseByCaseInfo.getAssertList();
        this.checkProperty(name, headers, "headers", false);

        // 从params中检查
        this.checkProperty(name, params, "params", false);

        // 从formData中检查
        this.checkProperty(name, formData, "formData", false);

        // 从formDataEncoded中检查
        this.checkProperty(name, formDataEncoded, "formDataEncoded", false);

        // 从raw中检查
        this.checkProperty(name, raw, "raw", false);

        // 从断言预期结果检查
        for (ApiTestAssertVo insertAssertVo : assertList) {
            String exceptedResult = insertAssertVo.getExceptedResult();
            this.checkProperty(name, exceptedResult, "断言预期结果", false);
        }
        // 如果是接口依赖，确保接口依赖前置用例的headers、params、form-data、form-data-encoded、raw、断言预期结果不包含当前依赖名称
        List<Integer> preCaseIdList = preCaseService.findApiTestPreCaseById(ifRelyDataDO.getRelyCaseId());
        log.info("获取到的前置用例：{}", preCaseIdList);
        for (Integer preCaseId : preCaseIdList) {
            ApiTestCaseInfoVo preCaseInfo = apiTestCaseService.findInterfaceCaseByCaseId(preCaseId);
            if (preCaseInfo == null){
                log.warn("前置用例{}不存在",preCaseId);
            }
            String preHeaders = preCaseInfo.getHeaders();
            String preParams = preCaseInfo.getParams();
            String preFormData = preCaseInfo.getFormData();
            String preFormDataEncoded = preCaseInfo.getFormDataEncoded();
            String preRaw = preCaseInfo.getRaw();
            List<ApiTestAssertVO> preCaseInfoAsserts = preCaseInfo.getAsserts();

            // 从headers中检查
            this.checkProperty(name, preHeaders, String.format("[%s]headers", preCaseId), true);

            // 从params中检查
            this.checkProperty(name, preParams, String.format("[%s]params", preCaseId), true);

            // 从formData中检查
            this.checkProperty(name, preFormData, String.format("[%s]formData", preCaseId), true);

            // 从formDataEncoded中检查
            this.checkProperty(name, preFormDataEncoded, String.format("[%s]formDataEncoded", preCaseId), true);

            // 从raw中检查
            this.checkProperty(name, preRaw, String.format("[%s]raw", preCaseId), true);

            // 从断言预期结果检查
            for (ApiTestAssertVO interfaceAssertVO : preCaseInfoAsserts) {
                String exceptedResult = interfaceAssertVO.getExceptedResult();
                this.checkProperty(name, exceptedResult, String.format("[%s]断言预期结果", preCaseId), true);
            }


        }
    }
    /**
     * 从不同域中检查依赖名称是否合法
     * @param dependencyName 依赖名称
     * @param property 域，如headers、params、formdata、formdataencoded、raw
     * @param propertyDesc 描述
     * @param isPreCase 是否是前置用例
     */
    private void checkProperty(String dependencyName, String property, String propertyDesc, boolean isPreCase) throws ParseException {
        for (String s : parser.extractDependencyName(property)) {
            if (s.equals(dependencyName)){
                if (!isPreCase){
                    throw new ParseException(String.format("被绑定用例%s不能引用当前依赖名称", propertyDesc));
                }else {
                    throw new ParseException(String.format("被绑定用例的前置用例%s不能引用当前依赖名称", propertyDesc));
                }
            }

        }
    }
}




